#pragma once

#include <vector>
#include "Channel.hpp"
#include <functional>
#include "Task.hpp"
#include <unistd.h>
  #include <sys/wait.h>
 #include <stdlib.h>
 
class ProcessPool
{
public:
    ProcessPool(int num, std::function<void()> work)
    :_procnum(num)
    ,_work(work)
    ,_proc(0)
    {}

    void Init()
    {
        for(int i=0; i<_procnum; i++)
        {
            //创建子进程前要先创建好管道，这样子进程才能将文件描述符继承下去
            int fd[2];
            pipe(fd);

            int pid= fork();
            if(pid==0)
            {
                //将所有的写端描述符全都关闭，包括进程池中的，还有未来得及加入的那个
                for(int i=0;i<_channels.size(); i++)
                {
                    close(_channels[i].Wfd());
                }
                close(fd[1]);

                dup2(fd[0], 0);
                _work();
                exit(0);
            }
            else
            {
                close(fd[0]);
                _channels.push_back({fd[1], pid});

            }
        }
    }

    int SelectProc()
    {
        return (_proc++)%_channels.size();
    }

    void DispatchTask(int num)
    {

        while(num--)
        {
            int t= tm.SelectTask();
            int p= SelectProc();

            _channels[p].Send(t);
            std::cout<<"发送任务："<<t<<" 给进程："<<_channels[p].Pid()<<std::endl;
            sleep(1);
        }

    }

     
    void CleanProcessPool()//父进程保留着唯一的，也是所有的写端描述符，如果父进程关闭，则管道写端的struct file就会释放，如果管道读端没有数据，那此时不再阻塞，而是直接返回0
    {
        for(auto& c: _channels)
        {
            close(c.Wfd());//子进程读管道时，管道没有数据，此时写端的struct file也已经释放，所以读端read会返回0，然后子进程退出
            waitpid(c.Pid(),nullptr, 0);
            std::cout<<"进程："<<c.Pid()<<" 退出"<<std::endl;
        }
    }

private:
    std::vector<Channel> _channels;//进程池
    int _procnum;//进程个数
    std::function<void()> _work;//子进程要执行的函数

    int _proc;//在进程池中选择进程
};


void Work()//子进程执行的
{
    while(true)
    {
        int t;
        int ret= read(0, &t, 4);
        if(ret==4 )
        {
            tm.Execute(t);
        }
        else if(ret==0)//管道写端的struct file已经释放
        {
            std::cout << "子进程: " << getpid() << " 退出" << std::endl;
            exit(0);
        }
        else
        {

        }
    }


}